//! 测试 (Testing)
//!
//! Rust 是一种系统级编程语言，它非常重视代码的正确性。因此，它包括了
//! 对单元测试和集成测试的一流支持。Rust 社区还制定了测试驱动开发 (TDD)
//! 的良好实践，因此 Rust 中的测试被认为是一种重要特性。

/// 1. 单元测试 (Unit Tests)
///
/// 单元测试是针对代码中最小可测试单元（通常是函数）的测试。在 Rust 中，
/// 单元测试通常放在被测试代码的同一个文件中，位于 `cfg(test)` 模块内。
pub fn unit_tests() {
    println!("=== 单元测试 (Unit Tests) ===");

    // 单元测试示例
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }

    // 测试函数通常放在 #[cfg(test)] 模块中
    // 这里我们直接演示测试逻辑
    assert_eq!(add(2, 3), 5);
    assert_eq!(add(-1, 1), 0);
    assert_eq!(add(0, 0), 0);

    println!("加法函数测试通过");
}

/// 2. 测试属性 (Test Attributes)
///
/// `#[test]` 属性将一个函数标记为测试函数
pub fn test_attributes() {
    println!("\n=== 测试属性 (Test Attributes) ===");

    // #[test] 属性标记测试函数
    // #[should_panic] 属性期望测试函数会 panic
    // #[ignore] 属性标记测试为忽略状态

    println!("测试属性说明:");
    println!("#[test] - 标记函数为测试函数");
    println!("#[should_panic] - 期望测试函数会 panic");
    println!("#[ignore] - 标记测试为忽略状态");
}

/// 3. 集成测试 (Integration Tests)
///
/// 集成测试位于项目根目录下的 `tests/` 目录中，它们从外部测试库的视角
/// 来测试代码，就像库的用户一样。
pub fn integration_tests() {
    println!("\n=== 集成测试 (Integration Tests) ===");

    // 集成测试示例说明
    println!("集成测试通常位于项目根目录下的 tests/ 目录中");
    println!("它们从外部视角测试库的公共 API");
    println!("每个测试文件都是一个单独的 crate");

    // 示例：在 tests/integration_test.rs 中
    // extern crate my_library;
    //
    // #[test]
    // fn it_adds_two() {
    //     assert_eq!(my_library::add(2, 2), 4);
    // }
}

/// 4. 文档测试 (Documentation Tests)
///
/// 文档测试是嵌入在文档注释中的代码示例，它们会被编译和执行。
pub fn documentation_tests() {
    println!("\n=== 文档测试 (Documentation Tests) ===");

    /// 这是一个文档测试示例
    ///
    /// ```
    /// let result = 2 + 2;
    /// assert_eq!(result, 4);
    /// ```
    ///
    /// # Panics
    ///
    /// 这个函数不会 panic
    ///
    /// ```
    /// # // 隐藏的行，不会显示在文档中
    /// let x = 5;
    /// assert_eq!(x, 5);
    /// ```
    fn example_function() -> i32 {
        42
    }

    println!("文档测试嵌入在文档注释中");
    println!("使用三个反引号 (```) 包围代码块");
    println!("以 # 开头的行是隐藏行，不会显示在文档中");
    println!("使用 cargo test --doc 运行文档测试");

    assert_eq!(example_function(), 42);
}

/// 5. 测试组织 (Test Organization)
///
/// Rust 社区通常将测试组织为单元测试和集成测试两种形式
pub fn test_organization() {
    println!("\n=== 测试组织 (Test Organization) ===");

    println!("单元测试:");
    println!("- 位于被测试代码的同一文件中");
    println!("- 测试私有函数和实现细节");
    println!("- 使用 #[cfg(test)] 模块");

    println!("\n集成测试:");
    println!("- 位于 tests/ 目录中");
    println!("- 只能测试公共 API");
    println!("- 每个文件都是独立的 crate");
}

/// 6. 测试输出 (Test Output)
///
/// Rust 测试默认会捕获输出，但可以通过 --nocapture 标志显示输出
pub fn test_output() {
    println!("\n=== 测试输出 (Test Output) ===");

    // 这个 println! 在正常测试运行中不会显示
    println!("这条消息只在使用 --nocapture 标志时显示");

    println!("默认情况下，测试运行时会捕获所有输出");
    println!("使用 'cargo test -- --nocapture' 显示输出");
    println!("使用 'cargo test -- --show-output' 显示成功测试的输出");
}

/// 7. 运行测试 (Running Tests)
///
/// 可以通过多种方式运行测试
pub fn running_tests() {
    println!("\n=== 运行测试 (Running Tests) ===");

    println!("运行所有测试:");
    println!("  cargo test");

    println!("\n运行特定测试:");
    println!("  cargo test test_name");

    println!("\n运行特定模块的测试:");
    println!("  cargo test module_name");

    println!("\n运行忽略的测试:");
    println!("  cargo test -- --ignored");

    println!("\n显示测试输出:");
    println!("  cargo test -- --nocapture");

    println!("\n并行运行测试:");
    println!("  cargo test -- --test-threads=4");
}

/// 8. 常用断言宏 (Assertion Macros)
///
/// Rust 提供了几个常用的断言宏用于测试
pub fn assertion_macros() {
    println!("\n=== 常用断言宏 (Assertion Macros) ===");

    // assert! 宏 - 检查条件是否为真
    assert!(1 == 1);
    assert!(2 + 2 == 4, "数学计算错误: 2 + 2 != 4");

    // assert_eq! 宏 - 检查两个值是否相等
    assert_eq!(2 + 2, 4);

    // assert_ne! 宏 - 检查两个值是否不相等
    assert_ne!(2 + 2, 5);

    println!("assert! - 检查条件是否为真");
    println!("assert_eq! - 检查两个值是否相等");
    println!("assert_ne! - 检查两个值是否不相等");
    println!("所有断言宏都接受可选的错误消息参数");
}

/// 9. 条件编译测试 (Conditional Compilation Tests)
///
/// 使用 #[cfg(test)] 条件编译属性可以包含仅在测试时编译的代码
pub fn conditional_compilation() {
    println!("\n=== 条件编译测试 (Conditional Compilation Tests) ===");

    // #[cfg(test)] 模块中的代码只在测试时编译
    // 这对于包含测试专用的辅助函数很有用

    println!("#[cfg(test)] - 仅在测试时编译的代码");
    println!("常用于测试模块中定义辅助函数");
    println!("有助于保持生产代码的简洁");
}

/// 主演示函数
pub fn demonstrate_testing() {
    unit_tests();
    test_attributes();
    integration_tests();
    documentation_tests();
    test_organization();
    test_output();
    running_tests();
    assertion_macros();
    conditional_compilation();
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_unit_tests() {
        // 我们测试的是函数能够被调用，而不是具体的 println! 输出
        fn add(a: i32, b: i32) -> i32 {
            a + b
        }

        assert_eq!(add(2, 3), 5);
        assert_eq!(add(-1, 1), 0);
        assert_eq!(add(0, 0), 0);
    }

    #[test]
    fn test_example_function() {
        fn example_function() -> i32 {
            42
        }

        assert_eq!(example_function(), 42);
    }

    #[test]
    #[ignore]
    fn ignored_test() {
        // 这个测试被忽略
        assert_eq!(2 + 2, 4);
    }

    #[test]
    #[should_panic(expected = "测试 panic!")]
    fn panicking_test() {
        // 这个测试应该 panic
        panic!("测试 panic!");
    }

    #[test]
    fn test_assertion_macros() {
        // 测试各种断言宏
        assert!(true);
        assert!(2 + 2 == 4, "数学计算错误");
        assert_eq!(2 + 2, 4);
        assert_ne!(2 + 2, 5);
    }

    // #[cfg(test)] 模块中的辅助函数示例
    fn helper_function_for_test() -> i32 {
        42
    }

    #[test]
    fn test_with_helper_function() {
        assert_eq!(helper_function_for_test(), 42);
    }

    #[test]
    fn test_demonstrate_testing() {
        // 测试主演示函数能被调用
        demonstrate_testing();
    }
}
